package com.logic.landseaserver.models.amortization;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.exception.ResultCodes.BillResultCode;
import com.logic.landseaserver.common.util.Arith;
import com.logic.landseaserver.common.util.DateUtil;

/**
 * 
 *
 * 项目名称：system-server</br>
 * 类名称：AmortizationCalculate</br>
 * 类描述：计算每天的数值</br>
 * 创建人：aaron.wang</br>
 * 创建时间：2017年11月11日 下午7:29:19</br>
 * 
 * @version 1.0
 *
 */
public abstract class AmortizationCalculate
{
    /**
     * 
     * [简要描述]：</br>
     * [详细描述]：</br>
     * [作者]：aaron.wang(2017-11-11)</br>
     *
     * @param start 开始日期
     * @param end 结束日期
     * @param monthMoney 月租金/月服务费（优惠后的）
     * @param currentMoney 本期账单交的总额
     * @param couponMoney 授权码总金额
     * @throws LandseaException
     *
     */
    public static Map<String, Double> calculate(Date start, Date end, double monthMoney, double currentMoney,
        Double couponMoney)
        throws LandseaException
    {
        start = DateUtil.dateConvertDate(start);
        end = DateUtil.dateConvertDate(end);
        
        Map<String, Double> contractDayMoneyMap = new LinkedHashMap<String, Double>();
        
        Calendar startC = Calendar.getInstance();
        startC.setTime(start);
        
        Calendar endC = Calendar.getInstance();
        endC.setTime(end);
        
        // 判断开始和结束是否为同年同月
        int startYear = startC.get(Calendar.YEAR);
        int startMonth = startC.get(Calendar.MONTH);
        int endYear = endC.get(Calendar.YEAR);
        int endMonth = endC.get(Calendar.MONTH);
        
        // 同年同月
        if (startYear == endYear && startMonth == endMonth)
        {
            List<String> days = displayDay(start, end);
            
            // 计算每天的钱
            double dayMoney = Arith.div(currentMoney, days.size(), 4);
            if (null != couponMoney)
            {
                double couponDayMoney = Arith.div(couponMoney, days.size(), 4);
                dayMoney = Arith.add(couponDayMoney, dayMoney);
            }
            
            setDayMoney(days, contractDayMoneyMap, dayMoney);
        }
        else
        {
            // 非同年同月
            
            Double couponDayMoney = null;
            if (null != couponMoney)
            {
                // 计算两个日期之间的天数
                int totalDays = DateUtil.daysBetween(start, end);
                couponDayMoney = Arith.div(couponMoney, totalDays, 4);
            }
            
            // 先计算第一个月
            int currentMonthTocalDays = startC.getActualMaximum(Calendar.DAY_OF_MONTH);
            // 由于可能不满月，计算出最后一天
            Date endDate = DateUtil.getInstence().getLastDayOfMonth(start);
            makeMonth(currentMonthTocalDays, start, endDate, monthMoney, contractDayMoneyMap, couponDayMoney);
            
            // 再计算末尾的月
            currentMonthTocalDays = endC.getActualMaximum(Calendar.DAY_OF_MONTH);
            Date startDate = DateUtil.getInstence().getFirstDayOfMonth(end);
            makeMonth(currentMonthTocalDays, startDate, end, monthMoney, contractDayMoneyMap, couponDayMoney);
            
            // 计算中间的月份，中间的月份可能有，可能没有,可能有多个
            Calendar date1Calendar = Calendar.getInstance();
            date1Calendar.setTime(DateUtil.getInstence().getFirstDayOfMonth(start));
            Calendar date2Calendar = Calendar.getInstance();
            date2Calendar.setTime(startDate);
            date1Calendar.add(Calendar.MONTH, 1);
            while (date1Calendar.before(date2Calendar))
            {
                currentMonthTocalDays = date1Calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
                Date currentTime = date1Calendar.getTime();
                startDate = DateUtil.getInstence().getFirstDayOfMonth(currentTime);
                endDate = DateUtil.getInstence().getLastDayOfMonth(currentTime);
                makeMonth(currentMonthTocalDays, startDate, endDate, monthMoney, contractDayMoneyMap, couponDayMoney);
                date1Calendar.add(Calendar.MONTH, 1);
            }
        }
        
        return contractDayMoneyMap;
    }
    
    // 计算月(可能不是满月)
    private static void makeMonth(int currentMonthTocalDays, Date startDay, Date endDay, double monthMoney,
        Map<String, Double> contractDayMoneyMap, Double couponDayMoney)
        throws LandseaException
    {
        List<String> days = displayDay(startDay, endDay);
        // 计算每天的钱
        double dayMoney = Arith.div(monthMoney, currentMonthTocalDays, 4);
        
        // 授权码平摊下来的
        if (null != couponDayMoney)
        {
            dayMoney = Arith.add(couponDayMoney, dayMoney);
        }
        
        setDayMoney(days, contractDayMoneyMap, dayMoney);
    }
    
    /**
     * 
     * [简要描述]：获取两个日期重叠的天日期数组</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2018-06-05)</br>
     *
     * @param startDate
     * @param endDate
     * @return
     * @throws LandseaException
     *
     */
    public static List<String> displayDay(Date startDate, Date endDate)
        throws LandseaException
    {
        try
        {
            List<String> result = new ArrayList<String>();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(startDate);
            
            while (!calendar.getTime().after(endDate))
            {
                result.add(DateUtil.convertYMDDate(calendar.getTime()));
                calendar.add(Calendar.DAY_OF_YEAR, 1);
            }
            
            return result;
        }
        catch (Exception e)
        {
            throw LandseaException.createException(BillResultCode.E004500021, e);
        }
    }
    
    private static void setDayMoney(List<String> days, Map<String, Double> contractDayMoneyMap, double dayMoney)
    {
        for (String s : days)
        {
            contractDayMoneyMap.put(s, dayMoney);
        }
    }
    
    /**
     * 
     * [简要描述]：获取最终的金额</br>
     * [详细描述]：</br>
     * [作者]：aaron.wang(2017-11-12)</br>
     *
     * @param startDate 摊销开始时间
     * @param endDate 摊销结束时间
     * @param dayMoneyMap 每天的金额map
     * @return
     *
     */
    public static double getResultMoney(Date startDate, Date endDate, Map<String, Double> dayMoneyMap)
    {
        if (null == dayMoneyMap || dayMoneyMap.isEmpty())
        {
            return 0.00d;
        }
        
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        
        double result = 0.0000d;
        
        while (!calendar.getTime().after(endDate))
        {
            String key = DateUtil.convertYMDDate(calendar.getTime());
            Double dayMoney = dayMoneyMap.get(key);
            if (null != dayMoney)
            {
                result = Arith.add(result, dayMoney);
            }
            calendar.add(Calendar.DAY_OF_YEAR, 1);
        }
        
        // 保留两位小数
        result = roundingDouble(result, 2);
        
        return result;
    }
    
    /**
     * 
     * [简要描述]：四舍五入后保留几位小数</br>
     * [详细描述]：</br>
     * [作者]：aaron.wang(2017-11-13)</br>
     *
     * @param d 待四舍五入的double值
     * @param length 保留几位小数
     * @return
     *
     */
    public static double roundingDouble(double d, int length)
    {
        length = length < 0 ? 0 : length;
        BigDecimal bg = new BigDecimal(d);
        double f1 = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        return f1;
    }
}
